package com.xiaocai.objview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ObjLoad {
    private static final String TAG = "ObjLoad";

    private String newmtl = "";

    private String usemtl = "";

    private List<Float> vList = new ArrayList<>();

    private List<Float> vtList = new ArrayList<>();

    private boolean stop;

    private Context context;

    private float maxX, minX, maxY, minY, maxZ, minZ;

    private Map<String, MTLInfo> mtlInfoMap = new HashMap<>();

    private Map<String, OBJInfo> objInfoMap = new HashMap<>();

    private List<ObjDrawInfo> objDrawInfoList = new ArrayList<>();

    private Bitmap defaultSTBitmap;

    public ObjLoad(Context context) {
        this.context = context;
        defaultSTBitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_8888);
    }

    private void reset() {
        objDrawInfoList.clear();
        vtList.clear();
        objInfoMap.clear();
        mtlInfoMap.clear();
        stop = false;
        newmtl = "";
        usemtl = "";
        vList.clear();
        maxX = 0;
        minX = 0;
        maxY = 0;
        minY = 0;
        maxZ = 0;
        minZ = 0;
    }

    public boolean readObjFile(String name, String parent) {
        reset();
        return readObj(name, parent);
    }

    private boolean readObj(String name, String parent) {

        BufferedReader reader = null;
        try {
            InputStream inputStream = null;
            if ("assets".equals(parent)) {
                inputStream = context.getAssets().open(name);
            } else {
                inputStream = new FileInputStream(new File(parent, name));
            }

            reader = new BufferedReader(new InputStreamReader(inputStream));

            String line;
            while ((line = reader.readLine()) != null && !stop) {
                String[] temp = line.split("[ ]+");
                switch (temp[0].trim()) {
                    case "mtllib":

                        try {
                            readObj(line.substring(line.indexOf(temp[1])), parent);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                        break;

                    case "newmtl":
                        newmtl = temp[1];
                        break;
                    case "map_Kd": {
                        try {
                            String kdName = line.substring(line.indexOf(temp[1]));
                            InputStream is = null;
                            if ("assets".equals(parent)) {
                                is = context.getAssets().open(kdName);
                            } else {
                                is = new FileInputStream(new File(parent, kdName));
                            }
                            Bitmap bitmap = BitmapFactory.decodeStream(is);
                            MTLInfo mtlInfo = getMTLInfo(newmtl);
                            mtlInfo.stBitmap = bitmap;
                            if (is != null) {
                                is.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                        break;

                    case "Kd": {

                        MTLInfo mtlInfo = getMTLInfo(newmtl);
                        mtlInfo.rgba[0] = Float.parseFloat(temp[1]);
                        mtlInfo.rgba[1] = Float.parseFloat(temp[2]);
                        mtlInfo.rgba[2] = Float.parseFloat(temp[3]);

                    }
                        break;
                    case "d":

                        MTLInfo mtlInfo = getMTLInfo(newmtl);
                        mtlInfo.rgba[3] = Float.parseFloat(temp[1]);

                        break;
                    case "v":
                        float x = Float.parseFloat(temp[1]);
                        float y = Float.parseFloat(temp[2]);
                        float z = Float.parseFloat(temp[3]);

                        vList.add(x);
                        vList.add(y);
                        vList.add(z);

                        maxX = Math.max(x, maxX);
                        maxY = Math.max(y, maxY);
                        maxZ = Math.max(z, maxZ);

                        minX = Math.min(x, minX);
                        minY = Math.min(y, minY);
                        minZ = Math.min(z, minZ);

                        break;
                    case "vt": {
                        float s = Float.parseFloat(temp[1]);
                        float t = 1 - Float.parseFloat(temp[2]);

                        vtList.add(s);
                        vtList.add(t);
                    }
                        break;

                    case "usemtl":

                        usemtl = temp[1];

                        break;
                    case "f":

                        for (int i = 1; i < temp.length && i <= 4; i++) {
                            if (i == 4) {
                                addVertex(temp[1]);
                                addVertex(temp[3]);
                            }
                            addVertex(temp[i]);
                        }

                        break;
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            if (reader != null) {
                reader.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return !stop;
    }

    private void addVertex(String s) {
        String[] temp = s.split("/");

        int index = Integer.parseInt(temp[0]) - 1;
        int stIndex = -1;
        if (temp.length > 1 && !TextUtils.isEmpty(temp[1])) {
            try {
                stIndex = Integer.parseInt(temp[1]) - 1;
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
        }

        OBJInfo objInfo = getOBJInfo(usemtl);
        MTLInfo mtlInfo = getMTLInfo(usemtl);

        float[] fs = new float[9];
        fs[0] = vList.get(3 * index);
        fs[1] = vList.get(3 * index + 1);
        fs[2] = vList.get(3 * index + 2);
        fs[3] = mtlInfo.rgba[0];
        fs[4] = mtlInfo.rgba[1];
        fs[5] = mtlInfo.rgba[2];
        fs[6] = mtlInfo.rgba[3];

        if (stIndex != -1) {
            fs[7] = vtList.get(2 * stIndex);
            fs[8] = vtList.get(2 * stIndex + 1);
        }

        objInfo.vList.add(fs);
    }

    private OBJInfo getOBJInfo(String usemtl) {
        OBJInfo objInfo = objInfoMap.get(usemtl);
        if (objInfo == null) {
            objInfo = new OBJInfo();

            objInfoMap.put(usemtl, objInfo);
        }

        return objInfo;
    }

    private MTLInfo getMTLInfo(String newmtl) {
        MTLInfo mtlInfo = mtlInfoMap.get(newmtl);
        if (mtlInfo == null) {
            mtlInfo = new MTLInfo();
            mtlInfo.name = newmtl;
            mtlInfo.stBitmap = null;
            mtlInfo.rgba = new float[4];
            mtlInfo.rgba[0] = 0.5f;
            mtlInfo.rgba[1] = 0.5f;
            mtlInfo.rgba[2] = 0.5f;
            mtlInfo.rgba[3] = 1;

            mtlInfoMap.put(newmtl, mtlInfo);
        }
        return mtlInfo;
    }

    public List<ObjDrawInfo> getObjDrawInfoList() {
        objDrawInfoList.clear();

        for (String newmtl : objInfoMap.keySet()) {
            OBJInfo objInfo = getOBJInfo(newmtl);
            MTLInfo mtlInfo = getMTLInfo(newmtl);

            int first = 0;
            int count = objInfo.vList.size();

            ObjDrawInfo drawInfo = new ObjDrawInfo();
            drawInfo.first = first;
            drawInfo.count = count;
            drawInfo.isTransparent = mtlInfo.rgba[3] < 1;
            drawInfo.stBitmap = mtlInfo.stBitmap;
            if (drawInfo.stBitmap == null) {
                drawInfo.stBitmap = defaultSTBitmap;
            }

            float[] fs = new float[count * 9];
            int index = 0;
            for (int i = first; i < first + count; i++) {
                float[] floats = objInfo.vList.get(i);
                fs[9 * index] = floats[0];
                fs[9 * index + 1] = floats[1];
                fs[9 * index + 2] = floats[2];
                fs[9 * index + 3] = floats[3];
                fs[9 * index + 4] = floats[4];
                fs[9 * index + 5] = floats[5];
                fs[9 * index + 6] = floats[6];
                fs[9 * index + 7] = floats[7];
                fs[9 * index + 8] = floats[8];
                index++;
            }
            drawInfo.vertexArray = new VertexArray(fs);

            if (drawInfo.isTransparent) {
                objDrawInfoList.add(drawInfo);
            } else {
                objDrawInfoList.add(0, drawInfo);
            }
        }

        return objDrawInfoList;
    }

    public void stop() {
        stop = true;
    }

    public float getZLength() {
        return maxZ - minZ;
    }

    public float getMaxZ() {
        return maxZ;
    }

    public float getMaxY() {
        return maxY;
    }

    public float getMaxX() {
        return maxX;
    }

    public float getMinX() {
        return minX;
    }

    public float getMinY() {
        return minY;
    }

    public float getMinZ() {
        return minZ;
    }

}
